﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Windows;
using CommomFunctions;
using System.Windows.Media;
using System.Windows.Controls;
using Seta;

namespace Listas
{
    public enum Operation
    {
        Insert,
        Remove
    }

    public class ControlVariables : Controls
    {
        /// <summary>
        /// contAdd -> number of elements in the List
        /// contElem -> counter for the automatic labeling. Keeps the index of the element that will be inserted to the List
        /// insertAt -> the position where the node will be inserted
        /// removeAt -> the position from where the node will be removed
        /// myList -> a system ArrayList that contains the elements of our List 
        /// firstTime -> indicates if it is the first time that the function gridSizeChanged is being executed
        /// </summary>
        private int contAdd = 0;
        private int contElem = 0;
        private int insertAt = 0;
        private int removeAt = 0;
        private ArrayList myList = new ArrayList();
        private bool firstTime = true;
                
        public int cont
        {
            get { return this.contAdd; }
        }

        public int insertAtPosition
        {
            get { return this.insertAt; }
            set { this.insertAt = value; }            
        }

        public int myListSize
        {
            get { return this.myList.Count; }
        }

        public int this[int i]
        {
            get {return (int) myList[i];}
        }

        /// <summary>
        /// Set all arrows, elements and the memory to hidden
        /// </summary>
        internal void setAllHidden()
        {
            myArrows.setAllHidden();
            elements.setAllHidden();
            memory.setAllHidden();
        }
        
        /// <summary>
        /// Enables or disables contained in "inds" according to "p"
        /// </summary>
        /// <param name="inds"></param>
        /// <param name="p"></param>
        internal void updateButtons(ButtonClicked[] inds, bool p)
        {
            Buttons.updateButtons(btns, inds, p);
        }            

        /// <summary>
        /// Sets the properties of the "Null Images" (images that represent the emptyness of the queue)
        /// when a queue is created
        /// </summary>
        internal void initializeNullImages()
        {
            myImages.setNullImagesMarginsAnimated(new Thickness(elements.getLabel(0).Margin.Left + 10, 0, elements.getLabel(0).Margin.Right + 10, 75),
                new Thickness(elements.getLabel(0).Margin.Left + 10, 674 / 15.0, elements.getLabel(0).Margin.Right + 10, 0),
                new Thickness(elements.getLabel(0).Margin.Left + 20, elements.getLabel(0).Margin.Top + 10, elements.getLabel(0).Margin.Right + 20, elements.getLabel(0).Margin.Bottom + 10));

            myImages.setHeight(WorkingImage.Null, 28);
            myImages.setNullImagesVisibility(Visibility.Visible);
        }        
        
        internal void resetCounters()
        {
            contElem = 0;
            contAdd = 0;
            myList.Clear();
        }

        internal void initComboBoxes()
        {
            myCbs.initCbs();
        }

        internal void initFreeMemory()
        {
            memory.initFreeMemory();            
        }

        internal void writeToTrace(ButtonClicked buttonClicked)
        {
            switch (buttonClicked)
            {
                case ButtonClicked.Create: txtTrace.writeTrace("\nCria nova lista vazia.\n", Brushes.RoyalBlue);
                    break;
                case ButtonClicked.Destroy: txtTrace.writeTrace("\nDestrói a lista.\n", Brushes.RoyalBlue);
                    break;
            }
        }
        
        internal void rebuild()
        {
             if (contAdd > 0)
             {
                 if (myImages.getVisibility(WorkingImage.Begin) == Visibility.Hidden)
                 {
                     myAnimations.animateImageOpacity(myImages.getImage(WorkingImage.Begin));
                     myAnimations.animateImageOpacity(myImages.getImage(WorkingImage.End));
                 }

                 myImages.setMargin(WorkingImage.Begin, new Thickness(elements.getLabel(0).Margin.Left + 10, 0, elements.getLabel(0).Margin.Right + 10, 75));
                 Images.setVisibility(WorkingImage.Begin, Visibility.Visible);

                 myImages.setMargin(WorkingImage.End, new Thickness(elements.getLabel(contAdd - 1).Margin.Left + 10, 674 / 15.0, elements.getLabel(contAdd - 1).Margin.Right + 10, 0));
                 Images.setVisibility(WorkingImage.End, Visibility.Visible);

                 Images.setVisibility(WorkingImage.BeginNull, Visibility.Hidden);
                 Images.setVisibility(WorkingImage.EndNull, Visibility.Hidden);
             }
             else
             {
                 if (!btns[(int)ButtonClicked.Create].IsEnabled)
                 {
                     myImages.setNullImagesVisibility(Visibility.Visible);
                     myImages.setImagesVisibility( Visibility.Hidden);
                 }
             }

            myImages.setMargin(WorkingImage.Null, new Thickness(elements.getLabel(contAdd).Margin.Left +20,elements.getLabel(contAdd).Margin.Top +10,
                                             elements.getLabel(contAdd).Margin.Right +20,elements.getLabel(contAdd).Margin.Bottom +10));
            myImages.setHeight(WorkingImage.Null, 28);

            myArrows.fillX1X2Positions(elements.getLabel(), firstTime);
        }

        internal void inicializaMemoriaLivre()
        {
            memory.initFreeMemory();
        }

        internal void insertionOperation(int pos)
        {
            if (testOverflow())
                return;

            if (contAdd == 0)
            {
                myAnimations.unAnimateImageOpacity(myImages.getImage(WorkingImage.Begin));
                myAnimations.unAnimateImageOpacity(myImages.getImage(WorkingImage.End));
                myAnimations.unAnimateImageOpacity(myImages.getImage(WorkingImage.BeginNull));
                myAnimations.unAnimateImageOpacity(myImages.getImage(WorkingImage.EndNull));                
            }

            Buttons.desabilitaBotoes(btns);
            
            insertAt = pos;
            contElem++;
                        
            myList.Insert(pos, contElem);
            txtTrace.writeTrace("Insere elemento " + contElem.ToString() + " na posicao " + (pos + 1).ToString(), Brushes.Goldenrod);

            memory.updateFreeMemory(contElem);

            myCbs.clearCbs();
            
            if (pos != 0)
                animateArrowOpacity(pos - 1);
            
            if (pos != contAdd)
                animateImageMargin(WorkingImage.End, new Thickness(elements.getLabel(contAdd).Margin.Left + 10,
                    674 / 15.0, elements.getLabel(contAdd).Margin.Right + 10, 0));
            
            if (pos == elements.numElements - 2)
                unAnimateImageOpacity(WorkingImage.End);
            
            animateImageMargin(WorkingImage.Null, new Thickness(elements.getLabel(contAdd + 1).Margin.Left + 20, elements.getLabel(contAdd + 1).Margin.Top + 10,
                                                     elements.getLabel(contAdd + 1).Margin.Right + 20, elements.getLabel(contAdd).Margin.Bottom + 10));
                                    
            for (int i = pos; i < contAdd; i++)
            {
                elements.setVisibility(i, Visibility.Hidden);
                myAnimations.animateArrow(i);
                
                myAnimations.animaDeslocamentoInsercao(i);
            }
            
            animatePositioning(pos);
            
            contAdd++;                 

            myCbs.updateCbs(contAdd, elements.numElements);
        }

        private void animatePositioning(int pos)
        {
            myAnimations.animatePositioning(pos, memory.freePos);
        }

        private void unAnimateImageOpacity(WorkingImage workingImage)
        {
            myAnimations.unAnimateImageOpacity(myImages.getImage(workingImage));            
        }

        private void animateImageMargin(WorkingImage workingImage, Thickness margemTempAux)
        {
            myAnimations.animateImageMargin(myImages.getImage(workingImage), margemTempAux);
        }

        private void animateArrowOpacity(int pos)
        {
            myAnimations.unAnimateArrowOpacity(myArrows, pos);            
        }
        
        private bool testOverflow()
        {
            if (contAdd == (elements.numElements - 1))
            {
                txtTrace.writeTrace("\nLista cheia.\nNão é possível inserir o elemento.\n", Brushes.Red);
                return true;
            }
            return false;
        }

        internal void RemovalOperation(int pos)
        {
            if (testUnderflow())
                return;

            Buttons.desabilitaBotoes(btns);
            
            txtTrace.writeTrace("Remove elemento " + myList[pos].ToString() + " da posicao " + (pos + 1).ToString(), Brushes.AntiqueWhite);

            memory.updateFreeMemory(myList[pos].ToString());
            
            myList.RemoveAt(pos);
            removeAt = pos;

            myCbs.clearCbs();

            if (contAdd != 1)
                myAnimations.animateImageMargin(myImages.getImage(WorkingImage.End), new Thickness(elements.getLabel(contAdd - 2).Margin.Left + 10, 674 / 15.0, elements.getLabel(contAdd - 2).Margin.Right + 10, 0));
            else
            {
                myAnimations.unAnimateImageOpacity(myImages.getImage(WorkingImage.Begin));
                myAnimations.unAnimateImageOpacity(myImages.getImage(WorkingImage.End));
            }

            Thickness tempMargin = new Thickness(elements.getLabel(contAdd - 1).Margin.Left + 20, elements.getLabel(contAdd - 1).Margin.Top + 10,
                                                elements.getLabel(contAdd - 1).Margin.Right + 20, elements.getLabel(contAdd - 1).Margin.Bottom + 10);
            
            if (pos == (contAdd - 1))
                myAnimations.animateImageMargin(myImages.getImage(WorkingImage.Null),tempMargin, 0.3) ;
            else
                myAnimations.animateImageMargin(myImages.getImage(WorkingImage.Null), tempMargin);
            
            myArrows.setVisibility(pos, Visibility.Hidden);
            elements.setVisibility(pos, Visibility.Hidden);

            for (int i = pos + 1; i < contAdd; i++)
            {
                elements.setVisibility(i, Visibility.Hidden);
                myAnimations.animaDeslocamentoRemocao(i);
                myAnimations.animaRemoveArrow(i);
            }

            if ((pos == 0) || (pos == contAdd - 1))
            {
                elements.setVisibility(pos, Visibility.Hidden);
            }

            myAnimations.animatePositioningRemoval(pos, memory.posMemo);

            contAdd--;

            myCbs.updateCbs(contAdd, elements.numElements);
            
        }

        private bool testUnderflow()
        {
            if (contAdd == 0)
            {
                txtTrace.writeTrace("Lista vazia. Não há elementos para remover.", Brushes.Red);
                return true;
            }
            return false;
        }


        internal Label getMemory(int pos)
        {
            return ((Label)memory.getMemory(pos));
        }

        internal Label getLabel(int pos)
        {
            return ((Label)elements.getLabel(pos));
        }

        internal void enableButtons()
        {
            Buttons.habilitaBotoes(btns);
        }

        internal void setNullImagesVisibility(Visibility visibility)
        {
            myImages.setNullImagesVisibility(visibility);
        }

        internal Image getImage(WorkingImage workingImage)
        {
            return myImages.getImage(workingImage);
        }

        internal void setLabelVisibility(int i, Visibility visibility)
        {
            elements.setVisibility(i, visibility);
        }

        internal void seLabelContent(int i, string p)
        {
            elements.setContent(i, p);
        }

        internal double getArrowPosition(PositionType positionType, int i)
        {
            return myArrows.getArrowPosition(positionType, i);
        }

        internal ArrowLine getArrow(int p)
        {
            return myArrows.getArrow(p);
        }

        internal void setAllArrowsHidden()
        {
            myArrows.setAllHidden();
        }
    }
}
